﻿/*---------------------------------------------------------------------------*/
/*                                                                           */
/* My_socket.h                                                               */
/*                                                                           */
/* Author : derrickhu (derrickhu@163.com)                                    */
/*                                                                           */
/*---------------------------------------------------------------------------*/

#ifndef MY_SOCKET_H_
#define MY_SOCKET_H_

// 标准库头文件
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>

#include <iostream>

#ifdef _LINUX

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netinet/ip.h>

#endif  // end _LINUX

#ifdef WIN32

#ifndef _WINDOWS_

#include <WinSock2.h>
#include <io.h>
#include <Ws2tcpip.h>

#endif

#endif  // end WIN32

// 跨平台的头文件
#include "cross_plat.h"

// 通用的一些宏
#ifndef INVALID_SOCKET
#define INVALID_SOCKET ~(0)        
#endif

//***********************************************************
// Method:    socket_init
// FullName:  socket_init
// Access:    public 
// Returns:   
// Description: 初始化socket,在Win下使用MySocket类之间一定需要先
//              调该函数
//***********************************************************
void socket_init();

const static int FILE_ENDING_LEN = 11;

// 文件结束符
static const char FILE_ENDING[FILE_ENDING_LEN + 1] = "@FileIsEnd@";

class MySocket
{
public:
    typedef enum
    {
        MYSOCKET_PROC_SUCC = 0,
        ERROR_INVALID_SOCKET = -10000,
        ERROR_INIT_SOCK,
        ERROR_ACCEPT_CLIENT,
        ERROR_BIND_PORT,
        ERROR_OPENFILE,
        ERROR_SET_FILE_POS,
        ERROR_READ_FILE,
        ERROR_SEEK_FILEPOS,
        ERROR_RECV_DATA,
        ERROR_SEND_DATA,
        ERROR_WRITE_FILE,
        ERROR_SEND_FILE,
    }SocketProcRet;

    // 定义可以使用的Socket的类型
    // 支持ipv4和ipv6
    typedef enum 
    {
        TCP4_SOCKET,
        UDP4_SOCKET,
        TCP6_SOCKET,
        UDP6_SOCKET,
        RAW_SOCKET
    }MySocketType;
public:
    MySocket(MySocketType m_type);
    ~MySocket();

    // 没有加深拷贝，感觉不怎会用这个函数，谁TM没事复制一个
    // 一个Socket啊
    MySocket& operator=(const MySocket &cp_sock);

    int initialize();

    int bind_port(uint16 port);

    int connect_svr(const int8 *ip, uint16 port);

    int svr_listen(const uint32 max_num);

    int accept_client(MySocket *new_socket);

    //***********************************************************
    // Method:    send_data
    // FullName:  MySocket::send_data
    // Access:    public 
    // Returns:   如果发送成功，返回0,
    //            如果发送失败，则返回-1
    // Description: 发送数据保证全发完
    // Parameter: const int8 * buf
    // Parameter: const uint32 buf_len
    //***********************************************************
    int send_data(const int8 *buf, const uint32 buf_len);

    //***********************************************************
    // Method:    recv_data
    // FullName:  MySocket::recv_data
    // Access:    public 
    // Returns:   返回0，接收成功；否则，返回-1；
    // Description: 保证接收buf_len长度的数据, 这样设计的目的，是为了可
    // 接收固定帧长的数据包，从而简化解包过程。
    // Parameter: int8 **buf
    // Parameter: const int buf_len
    // Parameter: const SOCKET m_comm_sock
    //***********************************************************
    int recv_data(int8 **buf, const int buf_len);

    //***********************************************************
    // Method:    recv_data
    // FullName:  MySocket::recv_data
    // Access:    public 
    // Returns:   接收到的数据长度
    // Description: 只是接收一次数据，不管它能收到多少
    // Parameter: int8 * * buf
    //***********************************************************
    int recv_data(int8 **buf);

    //***********************************************************
    // Method:    send_file
    // FullName:  MySocket::send_file
    // Access:    public 
    // Returns:   成功返回0，失败返回-1
    // Description: 发送文件函数，麻烦自已确认发送文件的位置不会超过文件
    // 的大小
    // Parameter: const int8 * file_name    文件路径
    // Parameter: const int32 send_pos 发送文件的位置
    // Parameter: int32 * send_num     实际发送的大小
    //***********************************************************
    int send_file(const int8 *file_name, const int32 send_pos, int32 *send_num);

    //***********************************************************
    // Method:    recv_file
    // FullName:  MySocket::recv_file
    // Access:    public 
    // Returns:   
    // Description: 如果文件不存在，则会创建新文件。如果文件存在的话，
    // 从文件尾开始写
    // Parameter: const int8 * file_name  文件路径
    // Parameter: int32 * recv_num        实际接收的大小
    // Parameter: int32 pos               文件接收的位置偏移
    //***********************************************************
    int recv_file(const int8 *file_name, int32 *recv_num);

    //***********************************************************
    // Method:    set_client_addr
    // FullName:  MySocket::set_client_addr
    // Access:    public 
    // Returns:   
    // Description: 设置client的地址
    // Parameter: const sockaddr_in & client_addr
    //***********************************************************
    void set_client_addr(const sockaddr_in &client_addr);

    void close_sock();
protected:
    int m_sendbytes_;
    int m_recvbytes_;
    SOCKET m_socket_;
    MySocketType m_socket_type_;
    int32 m_socket_domain_;
    struct sockaddr_in m_local_addr_;
    struct sockaddr_in m_client_addr_;
    struct sockaddr_in m_svr_addr_;
    int8 *m_buff_;

private:
    static const int MAX_BUFF_SIZE = 4096;

    static const int MAX_FILE_BUF = 1024;

private:
    bool is_valid_socket();

    int alloc_buf();

    inline int close_sockfd()
    {
        CLOSE(m_socket_);
    }
};

#endif